<?php

/**
 * Description of XQL_Connection
 *
 * @author prometheus
 */
class XQL_Connection extends PDO
{

	/**
	 * A rendszer által ezen a kapcsolaton keresztül elért táblanevek prefix-e.
	 * @var string
	 */
	protected $tablePrefix;

	/**
	 * A statement-ek létrehozásához használt PDOStatement osztály neve.
	 * @var string
	 */
	protected $pdoStmtClassName = 'XQL_Statement';

	/**
	 * Az adatbázis kapcsolat beállításainak a rendszer által az eléréséhez
	 * használt azonosító (álnév).
	 * @var string
	 */
	protected $alias;

	/**
	 * A kapcsolat létrehozásakor alkalmazott PDO attribútumok. Csak az "ATTR_"
	 * kezdetűek értendőek idetartozónak!
	 * @var array
	 * @link http://php.net/manual/en/pdo.constants.php
	 */
	protected $attributes;

	/**
	 * @var XQL_ObjectQuote
	 */
	protected $objectQuoter;

	/**
	 * Az adatbázist elérő PDO konnektor neve.
	 * @var String
	 */
	protected $connector = '';

	/**
	 * A következőként meghívott query() vagy prepare() metódus által
	 * példányosított PDOStatement-nek teszi lehetővé, hogy átvehesse a lekérést
	 * előállító XQL_Action példányt.
	 * @var XQL_Action
	 */
	protected $resultingAction;

	/**
	 * A kapcsolat létrejötte után egyszer lefutó - opcionális - szervizparancsok
	 * (MySQL esetében például a kapcsolat karakterkódolását beállító utasítások)
	 * helye.
	 * @var Array
	 */
	protected $commandsAfterConnected = array();

	/**
	 * Adatbázis kapcsolat konstruktora.
	 * A konstruktor több módon is paraméterezhető. Egyfelől használható a PDO
	 * konstruktorában megszokott paraméterekkel, másfelől úgy is, hogy az
	 * első paraméterben leíró-objektumot adunk meg, melynek lehetséges elemei:
	 * <ul>
	 * <li>URL: standard URL formájában megadott kapcsolati adatok -
	 * <i>konnektor</i>://<i>felhasználó</i>:<i>jelszó</i>@<i>hoszt</i>/<i>adatbázisnév</i></li>
	 * <li>DSN: A PDO által ismert standard DSN</li>
	 * <li>userName: A kapcsolódáshoz szükséges felhasználónév</li>
	 * <li>password: A kapcsolódáshoz szükséges felhasználóhoz tartozó jelszó</li>
	 * <li>options: A PDO által támogatott kapcsolat-beállítások</li>
	 * <li>alias: Az objektum azonosítására szolgáló egyedi álnév</li>
	 * <li>tablePrefix: A kapcsolat alatt elért adatbázis táblanevek előtagja</li>
	 * <li>pdoStmtClassName: A kapcsolat query() és prepare() metódusai által
	 * visszaadott statement objektumok osztálya</li>
	 * <li>commandsAfterConnected: A kapcsolat létrejötte után lefutó parancsok</li>
	 * </ul>
	 * @param Object|String $dsn Ha ez a paraméter objektum típusú, a többi
	 * paramétert a konstruktor figyelmen kívül hagyja!
	 * @param String $username
	 * @param String $password
	 * @param Array $driverOptions
	 */
	public function __construct($dsn, $username='', $password='', $driverOptions=array())
	{
		if (is_object($dsn))
		{
			$cd = $this->internalSetupObject($dsn);
			if (isset($dsn->alias)) $this->alias = $dsn->alias;
			if (isset($dsn->tablePrefix)) $this->tablePrefix = $dsn->tablePrefix;
			if (isset($dsn->pdoStmtClassName)) $this->pdoStmtClassName = $dsn->pdoStmtClassName;
			if (isset($dsn->commandsAfterConnected)) $this->commandsAfterConnected = $dsn->commandsAfterConnected;
			$dsn = $cd['dsn'];
			$username = $cd['user'];
			$password = $cd['pass'];
			$driverOptions = $cd['attrs'];
		}
		
		$this->internalSetupStandardPDO($dsn, $username, $password, (array)$driverOptions);
		parent::__construct($dsn, $username, $password, (array)$driverOptions);

		if (is_array($this->commandsAfterConnected))
		{
			foreach ($this->commandsAfterConnected as $cmd)
			{
				$this->exec($cmd);
			}
		}
	}
	
	protected function internalSetupStandardPDO($dsn, $username='', $password='', $driverOptions=array())
	{
		$this->attributes = (array)$driverOptions;
		if (!isset($this->attributes[PDO::ATTR_STATEMENT_CLASS]))
		{
			$this->attributes[PDO::ATTR_STATEMENT_CLASS] = array(
				$this->pdoStmtClassName,
				array($this)
			);
		}
		if (!isset($this->attributes[PDO::ATTR_ERRMODE]))
		{
			$this->attributes[PDO::ATTR_ERRMODE] = PDO::ERRMODE_EXCEPTION;
		}

		foreach ($this->attributes as $opt=>$val)
		{
			$this->setAttribute($opt, $val);
		}

		$this->connector = $this->internalGetConnector($dsn);
		if ($this->connector === FALSE)
		{
			// TODO: exception
		}

		$this->objectQuoter = XQL_ObjectQuote::factory($this->connector);
	}

	protected function internalSetupObject($cfg)
	{
		$dsn = '';
		$user = '';
		$pass = '';
		$attrs = array();
		if (isset($cfg->URL))
		{
			$parsedURL = parse_url($cfg->URL);
			$dsn = $parsedURL['scheme'].':host='.
				$parsedURL['host'].';dbname='.
				substr($parsedURL['path'], 1);
			if (isset($parsedURL['user']))
			{
				$user = $parsedURL['user'];
			}
			if (isset($parsedURL['pass']))
			{
				$pass = $parsedURL['pass'];
			}
		}
		if (isset($cfg->userName)) $user = $cfg->userName;
		if (isset($cfg->password)) $pass = $cfg->password;
		if (isset($cfg->options))
		{
			foreach ($cfg->options as $optName=>$optVal)
			{
				$attrs[constant($optName)] = $optVal;
			}
		}

		return array('dsn'=>$dsn, 'user'=>$user, 'pass'=>$pass, 'attrs'=>$attrs);
	}

	/**
	 * @param String $dsn
	 * @param String $username
	 * @param String $password
	 * @param Array $driverOptions
	 * @return XQL_Connection
	 */
	public static function create($dsn, $username='', $password='', $driverOptions=array())
	{
		return new XQL_Connection($dsn, $username, $password, $driverOptions);
	}

	/**
	 * Beállítja a kapcsolat álnevét, amelynek segítségével később könnyen
	 * azonosítható.
	 * @param String $alias
	 * @return XQL_Connection
	 */
	public function setAlias($alias)
	{
		$this->alias = $alias;
		return $this;
	}

	/**
	 * Visszaadja a kapcsolat álnevét, mellyel a kapcsolat azonosítható.
	 * @return String
	 */
	public function getAlias()
	{
		return $this->alias;
	}

	/**
	 * Beállítja a kapcsolat alatt használt általános táblanév előtagot.
	 * @param String $prefix
	 * @return XQL_Connection
	 */
	public function setTablePrefix($prefix)
	{
		$this->tablePrefix = $prefix;
		return $this;
	}

	/**
	 * Visszatér a kapcsolat alatt használt általános táblanév előtagot.
	 * @return String
	 */
	public function getTablePrefix()
	{
		return $this->tablePrefix;
	}

	/**
	 * Beállítja a kapcsolat által az adatszolgáltatáshoz használt PDOStatement
	 * osztály nevét. A beállítást követően az adott osztályból kerülnek
	 * példányosításra a query és prepare metódusok visszatérései.
	 * @param String $className
	 * @return XQL_Connection
	 */
	public function setStmtClass($className)
	{
		$this->pdoStmtClassName = $className;
		$this->setAttribute(PDO::ATTR_STATEMENT_CLASS, array($className, array($this)));
		return $this;
	}

	/**
	 * Visszatér az aktuálisan használt PDOStatement osztály nevével.
	 * @return String
	 */
	public function getStmtClass()
	{
		return $this->pdoStmtClassName;
	}

	/**
	 * Beállítja a resultingAction tulajdonság értékét.
	 * @param XQL_Action $action
	 * @return XQL_Connection
	 */
	public function setResultingAction(XQL_Action $action)
	{
		$this->resultingAction = $action;
		return $this;
	}

	/**
	 * Visszaadja a korábban beállított resultingAction értékét.
	 * @return XQL_Action
	 */
	public function getResultingAction()
	{
		return $this->resultingAction;
	}

	/**
	 * Visszaadja az adatbázis kapcsolathoz használt PDO konnektor nevét.
	 * @return String
	 */
	public function getConnector()
	{
		return $this->connector;
	}

	/**
	 * A konnektor típusának megfelelő kvótációval adja vissza az adatbázis-,
	 * tábla- és mezőneveket.
	 * @param String $sqlObject
	 * @return String
	 */
	public function quoteObject($sqlObject)
	{
		return $this->objectQuoter->quote($sqlObject);
	}

	public function completeTableName($tableName)
	{
		if ($tableName{0} == '!')
		{
			return substr($tableName, 1);
		}
		else
		{
			return $this->tablePrefix.$tableName;
		}
	}

	public function toBlobHex($string)
	{
		return '0x'.unpack('H*', $string);
	}

	/**
	 * Visszaadja az érvényes PDO DSN alapján az adatbázis konnektor nevét.
	 * @param String $dsn
	 * @return String
	 */
	protected function internalGetConnector($dsn)
	{
		$reGetServerType = '!^(?P<st>[a-z]*?)\:.*$!i';
		$matches = 0;
		$result = FALSE;
		if (preg_match($reGetServerType, $dsn, $matches))
		{
			$result = $matches['st'];
		}
		return strtolower($result);
	}
}

?>